#include "Stdafx.h"

#include "Engine.h"



 // general cache
////////////////////////////////////////////////////////////////////////////////////////


// cache allocation templates
template<typename BB> void Cache_Allocate(SEngineData* ed, long cacheSizeMB)
{
	assert(false);
}

template<> void Cache_Allocate<BitBoard>(SEngineData* ed, long cacheSizeMB)
{
	if(!ed->bUseCache)
		return;

	long itemCount = (cacheSizeMB * 1024 * 1024) / sizeof(CacheItem);
	itemCount /= 2; // use two-level cache

	ed->g_Cache = new CacheItem[itemCount];
	ed->g_Cache2 = new CacheItem[itemCount];
	ed->g_CacheItemCount = itemCount;

	memset(ed->g_Cache, 0, sizeof(CacheItem) * itemCount);
	memset(ed->g_Cache2, 0, sizeof(CacheItem) * itemCount);

	//ed->iCacheHit = 0;
	//ed->iCacheMiss = 0;
}

template<> void Cache_Allocate<BitBoard64>(SEngineData* ed, long cacheSizeMB)
{
	if(!ed->bUseCache)
		return;

	long itemCount = (cacheSizeMB * 1024 * 1024) / sizeof(CacheItem64);

	ed->g_Cache64 = new CacheItem64[itemCount];
	ed->g_CacheItemCount = itemCount;

	memset(ed->g_Cache64, 0, sizeof(CacheItem64) * itemCount);

	//ed->iCacheHit = 0;
	//ed->iCacheMiss = 0;
}

// cache free templates
template<typename BB> void Cache_Free(SEngineData* ed)
{
	assert(false);
}

template<> void Cache_Free<BitBoard>(SEngineData* ed)
{
	if(!ed->bUseCache)
		return;

	delete[] ed->g_Cache;
	ed->g_Cache = NULL;
	delete[] ed->g_Cache2;
	ed->g_Cache2 = NULL;
}

template<> void Cache_Free<BitBoard64>(SEngineData* ed)
{
	if(!ed->bUseCache)
		return;

	delete[] ed->g_Cache64;
	ed->g_Cache64 = NULL;
}

// clearing the cache
template<typename BB> void Cache_Clear(SEngineData *ed)
{
	assert(false);
}

template<> void Cache_Clear<BitBoard>(SEngineData *ed)
{
	if(!ed->bUseCache)
		return;

	memset(ed->g_Cache, 0, sizeof(CacheItem) * ed->g_CacheItemCount);
	memset(ed->g_Cache2, 0, sizeof(CacheItem) * ed->g_CacheItemCount);
}

template<> void Cache_Clear<BitBoard64>(SEngineData *ed)
{
	if(!ed->bUseCache)
		return;

	memset(ed->g_Cache64, 0, sizeof(CacheItem) * ed->g_CacheItemCount);
}

// cache lookup templates
template<typename BB>
bool Cache_Lookup(SEngineData* ed, ZOBRIST_KEY key, int depth, int nestedLevel, uint8& hashType, int& value, int& alpha, int& beta, bool bWhite, BB* bestMove)
{
	assert(false);
	return false;
}

template<>
bool Cache_Lookup<BitBoard>(SEngineData* ed, ZOBRIST_KEY key, int depth, int nestedLevel, uint8& hashType, int& value, int& alpha, int& beta, bool bWhite, BitBoard* bestMove)
{
	if(!ed->bUseCache)
		return false;

	ULONGLONG index = key % ed->g_CacheItemCount;

	// check the item in the first cache TT
	CacheItem* item = &(ed->g_Cache[index]);
	if( !( item->boardKey == key && item->bWhite == bWhite && item->iDepth >= depth) )
	{
		// check the item in the second cache TT
		item = &(ed->g_Cache2[index]);
		if( !( item->boardKey == key && item->bWhite == bWhite && item->iDepth >= depth) )
		{
			// if  depth is lower and otherwise the position is equal, get the TT move, 
			// first check first-level cache, than second-level cache
			item = &(ed->g_Cache[index]);
			if(item->boardKey == key && item->bWhite == bWhite)
			{
				if(bestMove)
					memcpy(bestMove, &(item->bbBestMove), sizeof(BitBoard));
			}
			else
			{
				item = &(ed->g_Cache2[index]);
				if(item->boardKey == key && item->bWhite == bWhite)
				{
					if(bestMove)
						memcpy(bestMove, &(item->bbBestMove), sizeof(BitBoard));
				}
			}

			ed->iCacheMiss++;
			return false;
		}
	}

	if(bestMove)
		memcpy(bestMove, &(item->bbBestMove), sizeof(BitBoard));

	int cacheValueLower = item->iValueLower;
	int cacheValueUpper = item->iValueUpper;

	// if end-game value
	if( (GAME_WIN - 500) < abs(item->iValueLower))
	{
		if(item->iValueLower > 0)
		{
			cacheValueLower = item->iValueLower - depth + item->iDepth;
		}
		else
		{
			cacheValueLower = item->iValueLower + depth - item->iDepth;
		}
	}
	if( (GAME_WIN - 500) < abs(item->iValueUpper))
	{
		if(item->iValueUpper > 0)
		{
			cacheValueUpper = item->iValueUpper - depth + item->iDepth;
		}
		else
		{
			cacheValueUpper = item->iValueUpper + depth - item->iDepth;
		}
	}

	// return the exact score
	if(cacheValueLower == cacheValueUpper)
	{
		value = cacheValueLower;
		ed->iCacheHit++;
		return true;
	}

	if(cacheValueLower >= beta)
	{
		value = cacheValueLower;
		ed->iCacheHit++;
		return true;
	}
	if(cacheValueUpper <= alpha)
	{
		value = cacheValueUpper;
		ed->iCacheHit++;
		return true;
	}

	alpha = max(alpha, cacheValueLower);
	beta = min(beta, cacheValueUpper);

	ed->iCacheMiss++;
	return false;
}

template<>
bool Cache_Lookup<BitBoard64>(SEngineData* ed, ZOBRIST_KEY key, int depth, int nestedLevel, uint8& hashType, int& value, int& alpha, int& beta, bool bWhite, BitBoard64* bestMove)
{
	if(!ed->bUseCache)
		return false;

	ULONGLONG index = key % ed->g_CacheItemCount;
	CacheItem64* item = &(ed->g_Cache64[index]);

	if(item->boardKey == key)
	{
		if(item->iDepth >= depth)
		{
			if(bestMove)
				memcpy(bestMove, &(item->bbBestMove), sizeof(BitBoard64));

			if(item->m_hashType == hashfVALUE)
			{
				value = item->iValue;				
				ed->iCacheHit++;
				return true;
			}
			else if(item->m_hashType == hashfALPHA && item->iValue <= alpha)
			{
				value = alpha;
				ed->iCacheHit++;
				return true;
			}
			else if(item->m_hashType == hashfBETA && item->iValue >= beta)
			{
				value = beta;
				ed->iCacheHit++;
				return true;
			}
		}
	}

	ed->iCacheMiss++;

	return false;
}

// cache store templates

template<typename BB>
void Cache_Store(SEngineData* ed, ZOBRIST_KEY key, int depth, int nestedLevel, int value, uint8 hashF, bool bWhite, BB* bestMove)
{
	assert(false);
}

template<>
void Cache_Store<BitBoard>(SEngineData* ed, ZOBRIST_KEY key, int depth, int nestedLevel, int value, uint8 hashF, bool bWhite, BitBoard* bestMove)
{
	if(!ed->bUseCache)
		return;

	// get items
	ULONGLONG index = key % ed->g_CacheItemCount;;
	CacheItem* item = &(ed->g_Cache[index]);
	CacheItem* item2 = &(ed->g_Cache2[index]);
	
	if(item->boardKey == key && item->iDepth > depth)
	{
		// use always replace of secondary cache
		item = item2;
	}
	else
	{
		// move the first level item to the second level item - always replace
		// use depth replace for the first level TT
		memcpy(item2, item, sizeof(CacheItem));
	}

	if(item->boardKey != key || item->iDepth < depth)
	{
		item->iValueLower = GAME_LOSS;
		item->iValueUpper = GAME_WIN;
	}

	item->boardKey = key;
	item->bWhite = bWhite;
	item->iDepth = depth;
	item->nestedLevel = nestedLevel;
	item->m_hashType = hashF;
	if(hashF == hashfALPHA)
		item->iValueUpper = value;
	if(hashF == hashfVALUE)
		item->iValueLower = item->iValueUpper = value;
	if(hashF == hashfBETA)
		item->iValueLower = value;

	if(bestMove)
		memcpy(&(item->bbBestMove), bestMove, sizeof(BitBoardCache));
}

template<>
void Cache_Store<BitBoard64>(SEngineData* ed, ZOBRIST_KEY key, int depth, int nestedLevel, int value, uint8 hashF, bool bWhite, BitBoard64* bestMove)
{
	if(!ed->bUseCache)
		return;

	ULONGLONG index = key % ed->g_CacheItemCount;;
	CacheItem64* item = &(ed->g_Cache64[index]);

	if(item->boardKey == key && item->iDepth > depth)
		return;
	item->boardKey = key;
	item->iDepth = depth;
	item->m_hashType = hashF;
	item->iValue = value;
	if(bestMove)
		memcpy(&(item->bbBestMove), bestMove, sizeof(BitBoardCache64));
}

 // Cache Dumping
//////////////////////////////////////////////////////////////////////////////

const wchar_t* cacheDumpFileName = L"c:\\Log\\cache_dump%d.log";
wchar_t szDumpSlice[MAX_PATH];

// finds nearest file name
void FindNearestDumpFile()
{
	FILE* fp = NULL;

	int i = 1;
	do
	{
		if(fp != NULL)
		{
			fclose(fp);
			fp = NULL;
		}

		swprintf(szDumpSlice, cacheDumpFileName, i);
		i++;

		fp = _wfopen(szDumpSlice, L"r");
	}
	while(fp != NULL);
}

// dumps cache
void DumpCache(SEngineData* ed)
{
	if(ed->bUseCache == false)
		return;

	// calculate space and hash types
	long typeAlpha = 0;
	long typeBeta = 0;
	long typeExact = 0;
	long numItems = 0;

	for(int i = 0; i < ed->g_CacheItemCount; i++)
	{
		CacheItem* item = &ed->g_Cache[i];
		if(item->boardKey != 0)
		{
			numItems++;
			if(item->m_hashType == hashfALPHA)
				typeAlpha++;
			else if(item->m_hashType == hashfBETA)
				typeBeta++;
			else if(item->m_hashType == hashfVALUE)
				typeExact++;
		}
	}

	// calculate percents
	int space = numItems * 100 / ed->g_CacheItemCount;
	int alphaPerc = typeAlpha * 100 / numItems;
	int betaPerc = typeBeta * 100 / numItems;
	int exactPerc = typeExact * 100 / numItems;

	// write log file
	FindNearestDumpFile();
	FILE* fp = _wfopen(szDumpSlice, L"w");
	fprintf(fp, "Alpha: %d, Beta: %d, Exact: %d,  Space: %d", alphaPerc, betaPerc, exactPerc, space);
	fclose(fp);
}






 // Evaluation cache item
//////////////////////////////////////////////////////////////////////////////////////////////////////////

//void EvalCache_Allocate(long itemCount)
//{
//	// make it power of two
//	if( itemCount & (itemCount - 1))
//	{
//		assert(false);
//		throw new std::string("Eval Cache is not power of two");
//	}
//
//	g_EvalCache = new EvalCacheItem[itemCount];
//	g_EvalCacheItemCount = itemCount;
//	g_EvalCacheMask = itemCount - 1;
//
//	memset(g_EvalCache, 0, sizeof(EvalCacheItem) * itemCount);
//}
//
//void EvalCache_Free()
//{
//	delete[] g_EvalCache;
//}
//
//bool EvalCache_Lookup(ZOBRIST_KEY key, int* pValue)
//{
//	ULONGLONG index = key & g_EvalCacheMask;
//	EvalCacheItem* item = &g_EvalCache[index];
//
//	if(item->boardKey == key)
//	{
//		*pValue = item->Value;
//		return true;
//	}
//
//	return false;
//}
//
//void EvalCache_Store(ZOBRIST_KEY key, int value)
//{
//	ULONGLONG index = key & g_EvalCacheMask;
//	EvalCacheItem* item = &g_EvalCache[index];
//	item->boardKey = key;
//	item->Value = value;
//}
//
